//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "CDeviceEventManager.h"
#define IncrementDllLockCount() do {} while(0)
#define DecrementDllLockCount() do {} while(0)
#include "_CDeviceEventManager.cpp"

static PDeviceEventIsrBlockNode s_pIsrMemBlock = NULL;
static const Int32 c_nOrder = 1;
static DLinkNode g_imFreed;

static DLinkNode *s_pDeviceEventDispatcherList = NULL;
static DLinkNode s_asynchEventQueueHead;

static DzReaderWriterLock s_dzEveDelegateRW;
static DzMutex s_dzAsyEveQueueMutex;

static IThread *s_pAsyncThread = NULL;
static DzEvent s_dzAsyncEvent(TRUE, UNSIGNALED);

static IDeviceEventDispatcher *GetIDeviceEventDispatcherFromEventNum(Int32 eventNum)
{
    DLinkNode *pHead = &s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)];
    PDeviceEventDispatcher ped = (PDeviceEventDispatcher)(pHead->Next());

    while (ped != (PDeviceEventDispatcher)pHead) {
        if (ped->uEventNum == eventNum) {
            return ped->pDeviceEventDispatcher;
        }
        ped = (PDeviceEventDispatcher)ped->Next();
    }

    return NULL;
}

Int32 AsynchEventHandler(void *pArg)
{
    PDeviceEventNodeBase penBase = NULL;
    IDeviceEventDispatcher *pEveDel = NULL;
    Boolean bIsrEvent;
    ipl_t oldIpl;
    WaitResult wr;

    while (TRUE) {
        oldIpl = DzRaiseCurrentIpl(IPL0);
        while (!s_asynchEventQueueHead.IsEmpty()) {
            penBase = (PDeviceEventNodeBase)(s_asynchEventQueueHead.First());
            penBase->Detach();
            bIsrEvent = penBase->bIsrParameter;
            DzLowerCurrentIpl(oldIpl);

            s_dzEveDelegateRW.WriterLock(&wr);
            pEveDel = GetIDeviceEventDispatcherFromEventNum(penBase->uEventNum);
            if (pEveDel) {
                if (!bIsrEvent) {
                    pEveDel->InvokeEventHandler(
                                    *((PDeviceEventNode)penBase)->peventData);
                }
                else {
                    MemoryBuf buf((Byte*)&(((PDeviceEventIsrNode)penBase)->isrpm),
                                    sizeof(EmIsrParameter));
                    pEveDel->InvokeEventHandler(buf);
                }
            }
            s_dzEveDelegateRW.WriterUnlock();

            if (!bIsrEvent) {
                PDeviceEventNode pen = (PDeviceEventNode)penBase;
                if (pen->peventData) {
                    MemoryBuf::Free(pen->peventData);
                }
                free(pen);
            }
            else {
                s_dzAsyEveQueueMutex.Lock(&wr);
                PDeviceEventIsrNode pin = (PDeviceEventIsrNode)penBase;
                g_imFreed.InsertLast((DLinkNode *)&s_pIsrMemBlock[pin->uNum]);
                s_dzAsyEveQueueMutex.Unlock();
            }

            oldIpl = DzRaiseCurrentIpl(IPL0);
        }

        DzLowerCurrentIpl(oldIpl);

        s_dzAsyncEvent.Wait(&wr, NULL);
    }

    return 0;
}

ECode CDeviceEventManager::SendEvent(
    /* [in] */ Int32 eventNum,
    /* [in] */ const MemoryBuf & ebEventData)
{
    WaitResult wr;
    s_dzEveDelegateRW.ReaderLock(&wr);

    IDeviceEventDispatcher *pEveDel = GetIDeviceEventDispatcherFromEventNum(eventNum);
    if (pEveDel) {
        pEveDel->InvokeEventHandler(ebEventData);
    }

    s_dzEveDelegateRW.ReaderUnlock();

    return NOERROR;
}

ECode CDeviceEventManager::PostEvent(
    /* [in] */ Int32 eventNum,
//    /* [in] */ Int32 nPriority,
    /* [in] */ const MemoryBuf & ebEventData)
{
    //save the event num
    PDeviceEventNode pDeviceEventNode = (PDeviceEventNode)malloc(sizeof(DeviceEventNode));
    if (NULL == pDeviceEventNode) return E_OUT_OF_MEMORY;
    memset(pDeviceEventNode, 0, sizeof(DeviceEventNode));
    pDeviceEventNode->uEventNum = eventNum;
    pDeviceEventNode->bIsrParameter = FALSE;

    //save the event data
    pDeviceEventNode->peventData = NULL;
    if (!ebEventData.IsNullOrEmpty()) {
        Int32 nDataLen = ebEventData.GetUsed();
        PMemoryBuf pBa = MemoryBuf::Alloc(nDataLen + 1);
        if (!pBa) {
            delete pDeviceEventNode;
            return E_OUT_OF_MEMORY;
        }
        pDeviceEventNode->peventData = pBa;
        pDeviceEventNode->peventData->Copy((MemoryBuf *)&ebEventData);
        pDeviceEventNode->peventData->SetUsed(nDataLen);
    }

    //put into the async event queue
    ipl_t oldIpl = DzRaiseCurrentIpl(IPL0);
    s_asynchEventQueueHead.InsertLast(
                    (DLinkNode *)((PDeviceEventNodeBase)pDeviceEventNode));
    DzLowerCurrentIpl(oldIpl);

    s_dzAsyncEvent.Notify(SIGNALED);
    s_dzAsyncEvent.Clear();
    return NOERROR;
}

ECode CDeviceEventManager::AddEventHandler(
    /* [in] */ Int32 eventNum,
//    /* [in] */ Int32 nOrder,
    /* [in] */ IDeviceEventDispatcher * pDelegate)
{
    if (NULL == pDelegate) return E_INVALID_ARGUMENT;

    PDeviceEventDispatcher ped = new DeviceEventDispatcher;
    if (NULL == ped) {
        return E_OUT_OF_MEMORY;
    }

    ped->pDeviceEventDispatcher = pDelegate;
    ped->pDeviceEventDispatcher->AddRef();

    ped->uEventNum = eventNum;

    //insert the event into the event pool
    WaitResult wr;
    s_dzEveDelegateRW.WriterLock(&wr);
    s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)].InsertFirst(ped);
    s_dzEveDelegateRW.WriterUnlock();

    return NOERROR;
}

ECode CDeviceEventManager::RemoveEventHandler(
    /* [in] */ Int32 eventNum,
    /* [in] */ IDeviceEventDispatcher * pDelegate)
{
    WaitResult wr;
    s_dzEveDelegateRW.WriterLock(&wr);
    DLinkNode *pHead = &s_pDeviceEventDispatcherList[EVENT_HASH_INDEX(eventNum)];
    PDeviceEventDispatcher ped = (PDeviceEventDispatcher)(pHead->Next());
    while (ped != (PDeviceEventDispatcher)pHead) {
        if (ped->uEventNum == eventNum) {
            ped->Detach();
            s_dzEveDelegateRW.WriterUnlock();
            if (ped->pDeviceEventDispatcher) {
                ped->pDeviceEventDispatcher->Release();
            }
            delete ped;
            return NOERROR;
        }
        ped = (PDeviceEventDispatcher)ped->Next();
    }
    s_dzEveDelegateRW.WriterUnlock();

    return NOERROR;
}

ECode CDeviceEventManager::Initialize()
{
    s_pDeviceEventDispatcherList = new DLinkNode[EVENT_HASH_LENGTH];
    if (NULL == s_pDeviceEventDispatcherList) {
        return E_OUT_OF_MEMORY;
    }

    UInt32 i;
    for (i = 0; i < EVENT_HASH_LENGTH; i++) {
        s_pDeviceEventDispatcherList[i].Initialize();
    }

    s_asynchEventQueueHead.Initialize();

    ECode ec = DzCreateKernelThread(AsynchEventHandler, (PVoid)this,
                        CreateThreadFlag_System, ThreadPriorityRank_Normal,
                        &s_pAsyncThread);
    if (FAILED(ec)) {
        delete s_pDeviceEventDispatcherList;
        return ec;
    }

    s_pIsrMemBlock = (PDeviceEventIsrBlockNode)DzAllocKernelPages(c_nOrder);
    if (NULL == s_pIsrMemBlock) {
        delete s_pDeviceEventDispatcherList;
        s_pAsyncThread->Release();
        return E_OUT_OF_MEMORY;
    }

    g_imFreed.Initialize();
    UInt32 mbNum = ((1u << c_nOrder) * PAGE_SIZE) / sizeof(DeviceEventIsrBlockNode);
    for (i = 0; i < mbNum; i++) {
        s_pIsrMemBlock[i].deviceEventIsrNode.bIsrParameter = TRUE;
        s_pIsrMemBlock[i].deviceEventIsrNode.uNum = i;
        g_imFreed.InsertLast((DLinkNode *)&s_pIsrMemBlock[i]);
    }

    return NOERROR;
}

CDeviceEventManager::~CDeviceEventManager()
{
    s_pAsyncThread->Release();

    PDeviceEventNodeBase pbn = NULL;
    while (!s_asynchEventQueueHead.IsEmpty()) {
        pbn = (PDeviceEventNodeBase)s_asynchEventQueueHead.First();
        pbn->Detach();
        if (!pbn->bIsrParameter) {
            delete ((PDeviceEventNode)pbn);
        }
    }

    UInt32 i;
    PDeviceEventDispatcher ped;
    for (i = 0; i < EVENT_HASH_LENGTH; i++) {
        while (!s_pDeviceEventDispatcherList[i].IsEmpty()) {
            ped = (PDeviceEventDispatcher)(s_pDeviceEventDispatcherList[i].First());
            ped->Detach();
            delete ped;
        }
    }

    delete s_pDeviceEventDispatcherList;

    DzFreeKernelPages(s_pIsrMemBlock, c_nOrder);
}

EXTERN ECode EmPostEventByIsr(
    /* [in] */ UInt32 uEventNum,
    /* [in] */ PEmIsrParameter pEmIsrParm)
{
    ipl_t oldIpl = DzRaiseCurrentIpl(IPL0);
    if (!g_imFreed.IsEmpty()) {
        PDeviceEventIsrBlockNode peb = (PDeviceEventIsrBlockNode)g_imFreed.First();
        peb->Detach();
        peb->deviceEventIsrNode.uEventNum = uEventNum;
        memcpy((PVoid)&(peb->deviceEventIsrNode.isrpm), pEmIsrParm,
                        sizeof(EmIsrParameter));
        s_asynchEventQueueHead.InsertLast(
            (DLinkNode *)((PDeviceEventNodeBase) & (peb->deviceEventIsrNode)));
    }
    DzLowerCurrentIpl(oldIpl);

    s_dzAsyncEvent.NotifyByIsr(SIGNALED);
    s_dzAsyncEvent.Clear();

    return NOERROR;
}
